type op = (* Operators *) 
	Add 
	| Sub 
	| Mult 
	| Div 
	| Equal
	| Neq
	| Less 
	| Leq 
	| Greater 
	| Geq 
	| And 
	| Or 
	| Mod

type expr = (* Expressions *)
	Id of string (* foo *) 
	| IntLiteral of int (* 42 *)
	| BoolLiteral of bool (* true *)
	| Binop of expr * op * expr (* a + b *)
	| Assign of expr * expr (* foo = 42 *)
	| Call of string * expr list (* foo(1, 25 *)
	| Noexpr (* for(;;) *)
	| Not of expr (* !x *)
	| StringLiteral of string (* "hello" *)
	| CardLiteral of string (* S2 *)
	| ListLiteral of expr list (* [1,2,3,4] *)
	| LabelLiteral of string (* label A - for break fix *)

type stmt = (* Statements *)
	Expr of expr (* foo = bar + 3 *)
	| Return of expr (* return 42 *) 
	| If of expr * stmt * stmt (* if (foo == 42) {} else {} *)
	| Block of stmt list (* {...} *)
	| For of expr * expr * expr * stmt (* for (i=0;i<10;i++) { ... } *)
	| While of expr * stmt (* while (i<10) { i = i+1 } *)

type var_decl = {
	varname : string; (* Name of the variable *)
	vartype : string; (* Name of variable type *)
}

type func_decl = {
		fname : string; (* Name of the function *)
		freturn : string; (* Name of return type *)
		formals : var_decl list; (* Formal argument names *)
		locals : var_decl list; (* Locally defined variables *)
		body : stmt list; (* Statement List *)
}

type program = var_decl list * func_decl list (* global vars, funcs *)
	
(* Methods below are to print out the AST for testing/debugging reasons *)	
	
(* method for printing expressions *)	
let rec string_of_expr = function
    IntLiteral(l) -> string_of_int l
	| BoolLiteral(b) -> string_of_bool b
  | Id(s) -> s
  | Binop(e1, o, e2) ->
      string_of_expr e1 ^ " " ^
      (match o with
			Add -> "+" 
			| Sub -> "-" 
			| Mult -> "*" 
			| Div -> "/"
      | Equal -> "==" 
			| Neq -> "!=" 
			| And -> "&&" 
			| Or -> "||" 
			| Mod -> "%"
      | Less -> "<" 
			| Leq -> "<=" 
			| Greater -> ">" 
			| Geq -> ">=") ^ " " ^ string_of_expr e2
  | Assign(v, e) -> string_of_expr v ^ " = " ^ string_of_expr e
  | Call(f, el) ->
      f ^ "(" ^ String.concat ", " (List.map string_of_expr el) ^ ")"
  | Noexpr -> ""
	| StringLiteral(s) -> s
	| CardLiteral(c) -> c
	| ListLiteral(p) -> "[" ^ String.concat "," (List.map string_of_expr p) ^ "]"
	| LabelLiteral(a) -> a
	| Not(n) -> "!" ^ string_of_expr n

(* method for printing statements *)	
let rec string_of_stmt = function
    Block(stmts) ->
      "{\n" ^ String.concat "" (List.map string_of_stmt stmts) ^ "}\n"
  | Expr(expr) -> string_of_expr expr ^ ";\n";
  | Return(expr) -> "return " ^ string_of_expr expr ^ ";\n";
  | If(e, s, Block([])) -> "if (" ^ string_of_expr e ^ ")\n" ^ string_of_stmt s
  | If(e, s1, s2) ->  "if (" ^ string_of_expr e ^ ")\n" ^
      string_of_stmt s1 ^ "else\n" ^ string_of_stmt s2
  | For(e1, e2, e3, s) ->
      "for (" ^ string_of_expr e1  ^ " ; " ^ string_of_expr e2 ^ " ; " ^
      string_of_expr e3  ^ ") " ^ string_of_stmt s
  | While(e, s) -> "while (" ^ string_of_expr e ^ ") " ^ string_of_stmt s
	
(* method for printing variable decls *)	
let string_of_var_decl var_decl = 
	"varname: " ^ var_decl.varname ^ "\nvartype: " ^ var_decl.vartype ^ "\n"
	
(* method for printing function decls *)		
let string_of_fdecl fdecl =
	"\nfreturn: " ^ fdecl.freturn ^ "\nfname: "  ^ fdecl.fname ^  "(" ^
	String.concat " " (List.map string_of_var_decl fdecl.formals) ^ ")\n{\n" ^
  String.concat " " (List.map string_of_var_decl fdecl.locals) ^ " "^
  String.concat " " (List.map string_of_stmt fdecl.body) ^
  "}"

(* method for printing program - list of var_decl and func_decl *)	
let string_of_program (vars, funcs) = 
	"VARS: \n" ^ String.concat " " (List.map string_of_var_decl vars) ^ " \n\nFUNCTIONS: " ^
	String.concat "\n" (List.map string_of_fdecl funcs)